package net.dgg.rz.common.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import net.dgg.rz.common.excption.CommonExcption;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.Converter;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * describe: 实体工具类，目前copy不支持map、list
 * Created by yan.x on 2018/3/13 18:23.
 **/
public final class BeanUtils extends org.springframework.beans.BeanUtils {

    private static final Map<String, BeanCopier> BEAN_COPIERS = new ConcurrentHashMap<>();

    private BeanUtils() {
    }

    /**
     * 实例化对象
     *
     * @param clazz 类
     * @return 对象
     */
    public static <T> T newInstance(Class<?> clazz) {
        return (T) instantiateClass(clazz);
    }

    /**
     * 获取缓存key
     *
     * @param class1
     * @param class2
     * @param useConverter
     * @return
     */
    private static String generateKey(Class<?> class1, Class<?> class2, boolean useConverter) {
        String key = class1.getName() + "@" + class2.getName();
        if (useConverter) {
            key += "@" + useConverter;
        }
        return key;
    }

    /**
     * 获取缓存BeanCopier
     *
     * @param sourceClazz  : 拷贝源类
     * @param targetClazz  : 目标类
     * @param useConverter : 是否使用转换器
     * @return
     */
    private static BeanCopier getBeanCopier(Class sourceClazz, Class targetClazz, boolean useConverter) {
        String cacheKey = generateKey(sourceClazz, targetClazz, useConverter);
        BeanCopier copier = null;
        // 缓存中有BeanCopier 直接拿来用,没有就创建并放入缓存
        if (BEAN_COPIERS.containsKey(cacheKey)) {
            copier = BEAN_COPIERS.get(cacheKey);
        } else {
            copier = BeanCopier.create(sourceClazz, targetClazz, useConverter);
            BEAN_COPIERS.putIfAbsent(cacheKey, copier);
        }
        return copier;
    }

    /**
     * copy 对象属性到另一个对象，默认不使用Convert
     *
     * @param source      : 拷贝源对象
     * @param targetClazz : 目标类名
     * @return T
     */
    public static <T> T copy(Object source, Class<T> targetClazz) {
        BeanCopier copier = getBeanCopier(source.getClass(), targetClazz, false);
        T to = newInstance(targetClazz);
        copier.copy(source, to, null);
        return to;
    }

    /**
     * 拷贝对象
     *
     * @param source : 拷贝源对象
     * @param target : 目标对象
     */
    public static void copy(Object source, Object target) {
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass(), false);
        copier.copy(source, target, null);
    }


    /**
     * 拷贝对象
     *
     * @param source    : 拷贝源对象
     * @param target    : 目标对象
     * @param converter : 转换器
     */
    public static void copy(Object source, Object target, Converter converter) {
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass(), true);
        copier.copy(source, target, converter);
    }

    /**
     * 获取bean 属性
     *
     * @param clazz : 对象
     * @return
     */
    public static List getPropertyName(Class<?> clazz) {
        List<String> list = new ArrayList<>();
        PropertyDescriptor[] beanPds = getPropertyDescriptors(clazz);
        for (PropertyDescriptor beanPd : beanPds) {
            String name = beanPd.getName();
            if ("serialVersionUID".equalsIgnoreCase(name) || "Class".equalsIgnoreCase(name)) {
	            continue;
            }
            list.add(name);
        }
        return list;
    }

    /**
     * 获取bean 属性和属性类型
     *
     * @param clazz : 对象
     * @return
     */
    public static Map getNameAndType(Class<?> clazz) {
        Map<String, String> map = new HashMap<>();
        PropertyDescriptor[] beanPds = getPropertyDescriptors(clazz);
        for (PropertyDescriptor beanPd : beanPds) {
            String typeName = beanPd.getPropertyType().getTypeName();
            if ("java.lang.Class".equalsIgnoreCase(typeName)) {
	            continue;
            }
            String name = beanPd.getName();
            if ("serialVersionUID".equalsIgnoreCase(name)) {
	            continue;
            }
            map.put(name, typeName);
        }
        return map;
    }

    /**
     * 将对象装成map形式
     *
     * @param src
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static Map toMap(Object src) {
        return BeanMap.create(src);
    }

    /**
     * 将map 转为 bean
     */
    public static <T> T toBean(Map<String, Object> beanMap, Class<T> valueType) {
        T bean = BeanUtils.newInstance(valueType);
        PropertyDescriptor[] beanPds = getPropertyDescriptors(valueType);
        for (PropertyDescriptor propDescriptor : beanPds) {
            String propName = propDescriptor.getName();
            // 过滤class属性
            if ("class".equals(propName)) {
                continue;
            }
            if (beanMap.containsKey(propName)) {
                Method writeMethod = propDescriptor.getWriteMethod();
                if (null == writeMethod) {
                    continue;
                }
                Object value = beanMap.get(propName);
                if (!writeMethod.isAccessible()) {
                    writeMethod.setAccessible(true);
                }
                try {
                    writeMethod.invoke(bean, value);
                } catch (Throwable e) {
                    throw new CommonExcption("Could not set property '" + propName + "' to bean", e);
                }
            }
        }
        return bean;
    }
}